Linux网络编程【14】(基于UDP的网络聊天室) 您所在的位置:网站首页 linux c线程同步 Linux网络编程【14】(基于UDP的网络聊天室)

Linux网络编程【14】(基于UDP的网络聊天室)

2023-03-21 00:10| 来源: 网络整理| 查看: 265

基于UDP的网络聊天室 功能:

有新用户登录,其他在线的用户可以收到登录信息

有用户群聊,其他在线的用户可以收到群聊信息

有用户退出,其他在线的用户可以收到退出信息

服务器可以发送系统信息

提示:

客户端登录之后,为了实现一边发送数据一边接收数据,可以使用多进程或者多线程

服务器既可以发送系统信息,又可以接收客户端信息并处理,可以使用多进程或者多线程

服务器需要给多个用户发送数据,所以需要保存每一个用户的信息,使用链表来保存

数据传输的时候要定义结构体,结构体中包含操作码、用户名以及数据

//UDP网络编程之服务器 #include #include #include #include #include #include #include #include #define N 128 #define ERRLOG(errmsg) do{\ perror(errmsg);\ printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\ exit(1);\ }while(0) typedef struct{ int code; //操作码 char name[32]; //用户名 char text[32]; //消息 }MSG; //链表结点结构体 typedef struct node{ struct sockaddr_in addr; //数据域,用于保存每一个在线用户的信息 struct node *next; }linklist; linklist *linklistcreate(); void do_login(int sockfd, MSG msg, linklist *h, struct sockaddr_in clientaddr); void do_chat(int sockfd, MSG msg, linklist *h, struct sockaddr_in clientaddr); void do_quit(int sockfd, MSG msg, linklist *h, struct sockaddr_in clientaddr); int main(int argc, char const *argv[]) { if(argc < 3) { fprintf(stderr, "Usage: %s \n", argv[0]); exit(1); } int sockfd; struct sockaddr_in serveraddr, clientaddr; socklen_t addrlen = sizeof(serveraddr); char buf[N] = {0}; //第一步:创建套接字 if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { ERRLOG("socket error"); } //第二步:填充服务器网络信息结构体 serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(argv[1]); serveraddr.sin_port = htons(atoi(argv[2])); //第三步:将套接字与服务器网络信息结构体绑定 if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1) { ERRLOG("bind error"); } //创建子进程,实现一边发送系统信息,一边接收数据并处理 pid_t pid; if((pid = fork()) == -1) { ERRLOG("fork error"); } else if(pid > 0) //父进程负责发送系统信息 { //将父进程看做是一个客户端,按照群聊的形式将数据发送给子进程 MSG msg; msg.code = 2; strcpy(msg.name, "server"); while(1) { fgets(msg.text, 32, stdin); msg.text[strlen(msg.text) - 1] = '\0'; if(sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, addrlen) == -1) { ERRLOG("sendto error"); } } } else //子进程负责接收数据并处理 { MSG msg; linklist *h = linklistcreate(); while(1) { if(recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&clientaddr, &addrlen) == -1) { ERRLOG("recvfrom error"); } printf("code:%d, name:%s, text:%s\n", msg.code, msg.name, msg.text); //根据接收到的数据做出相应的处理 //登录:1 群聊:2 退出:3 switch(msg.code) { case 1: //登录操作 do_login(sockfd, msg, h, clientaddr); break; case 2: //群聊操作 do_chat(sockfd, msg, h, clientaddr); break; case 3: //退出操作 do_quit(sockfd, msg, h, clientaddr); break; } } } return 0; } linklist *linklistcreate() { linklist *h = (linklist *)malloc(sizeof(linklist)); h->next = NULL; return h; } void do_login(int sockfd, MSG msg, linklist *h, struct sockaddr_in clientaddr) { char buf[N] = {0}; //组包将新用户登录的信息发送给其他在线的用户 sprintf(buf, "----------------------%s上线了----------------------", msg.name); linklist *p = h; //遍历链表,给每一个数据域发送数据 while(p->next != NULL) { p = p->next; if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&p->addr, sizeof(struct sockaddr_in)) == -1) { ERRLOG("sendto error"); } } //将新登录用户的信息保存在链表中 linklist *temp = (linklist *)malloc(sizeof(linklist)); temp->addr = clientaddr; temp->next = NULL; temp->next = h->next; h->next = temp; } void do_chat(int sockfd, MSG msg, linklist *h, struct sockaddr_in clientaddr) { //组包将用户的群聊信息发送给其他用户 //组包 char buf[N] = {0}; sprintf(buf, "%s: %s", msg.name, msg.text); //遍历链表将信息发送给其他用户 linklist *p = h; while(p->next != NULL) { p = p->next; //发送者不接收自己发送的数据 if(memcmp(&p->addr, &clientaddr, sizeof(clientaddr)) != 0) { if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&p->addr, sizeof(struct sockaddr_in)) == -1) { ERRLOG("sendto error"); } } } } void do_quit(int sockfd, MSG msg, linklist *h, struct sockaddr_in clientaddr) { char buf[N] = {0}; //组包将用户退出的信息发送给其他在线的用户 sprintf(buf, "----------------------%s下线了----------------------", msg.name); //遍历链表将信息发送给其他用户 linklist *p = h; linklist *temp; while(p->next != NULL) { //从链表中删除退出用户的信息 if(memcmp(&p->next->addr, &clientaddr, sizeof(clientaddr)) == 0) { temp = p->next; p->next = temp->next; free(temp); temp = NULL; } else { if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&p->next->addr, sizeof(struct sockaddr_in)) == -1) { ERRLOG("sendto error"); } p = p->next; } } } 复制代码 //UDP网络编程之客户端 #include #include #include #include #include #include #include #include #include #define N 128 #define ERRLOG(errmsg) do{\ perror(errmsg);\ printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\ exit(1);\ }while(0) typedef struct{ int code; //操作码 char name[32]; //用户名 char text[32]; //消息 }MSG; int main(int argc, char const *argv[]) { if(argc < 3) { fprintf(stderr, "Usage: %s \n", argv[0]); exit(1); } int sockfd; struct sockaddr_in serveraddr; socklen_t addrlen = sizeof(serveraddr); //第一步:创建套接字 if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { ERRLOG("socket error"); } //第二步:填充服务器网络信息结构体 serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(argv[1]); serveraddr.sin_port = htons(atoi(argv[2])); MSG msg; //登录操作 //设置操作码并输入用户名发送给服务器实现登录操作 msg.code = 1; //输入用户名实现登录操作 printf("您输入用户名:"); fgets(msg.name, 32, stdin); msg.name[strlen(msg.name) - 1] = '\0'; if(sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, addrlen) == -1) { ERRLOG("sendto error"); } //创建子进程,实现一边发送数据,一边接收数据 pid_t pid; if((pid = fork()) == -1) { ERRLOG("fork error"); } else if(pid > 0) //父进程负责发送数据 { while(1) { //输入数据,判断是群聊还是退出操作 fgets(msg.text, 32, stdin); msg.text[strlen(msg.text) - 1] = '\0'; //退出操作 if(strcmp(msg.text, "quit") == 0) { //设置操作码 msg.code = 3; //将退出消息发送给服务器 if(sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, addrlen) == -1) { ERRLOG("sendto error"); } //客户端退出 kill(pid, SIGKILL); exit(0); } //群聊操作 else { //设置操作码 msg.code = 2; //将群聊数据发送给服务器 if(sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, addrlen) == -1) { ERRLOG("sendto error"); } } } } else //子进程负责接收数据 { char buf[N] = {0}; while(1) { if(recvfrom(sockfd, buf, N, 0, NULL, NULL) == -1) { ERRLOG("recvfrom error"); } printf("%s\n", buf); } } return 0; } 复制代码


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有